package com.samphanie.auiu.library.restful

import com.samphanie.auiu.library.cache.AuiuStorage
import com.samphanie.auiu.library.executor.AuiuExecutor
import com.samphanie.auiu.library.log.AuiuLog
import com.samphanie.auiu.library.restful.annotation.CacheStrategy
import com.samphanie.auiu.library.utils.MainHandler

/**
 * @author Dries
 *
 * 代理CallFactory创建出来的call对象，从而实现拦截器的派发动作
 **/
class Scheduler(
    private val callFactory: AuiuCall.Factory,
    private val interceptors: MutableList<AuiuInterceptor>
) {
    fun newCall(request: AuiuRequest): AuiuCall<*> {
        val newCall: AuiuCall<*> = callFactory.newCall(request)

        return ProxyCall(newCall, request)
    }

    internal inner class ProxyCall<T>(
        private val delegate: AuiuCall<T>,
        private val request: AuiuRequest
    ) : AuiuCall<T> {

        override fun execute(): AuiuResponse<T> {
            dispatchInterceptor(request, null)
            if (request.cacheStrategy == CacheStrategy.CACHE_FIRST) {
                val cacheResponse = readCache<T>()
                if (cacheResponse.data != null) {
                    return cacheResponse
                }
            }
            val response = delegate.execute()
            saveCacheIfNeed(response)

            dispatchInterceptor(request, response)
            return response
        }

        override fun enqueue(callback: AuiuCallback<T>) {
            dispatchInterceptor(request, null)

            if (request.cacheStrategy == CacheStrategy.CACHE_FIRST) {
                AuiuExecutor.executor(runnable = {
                    val cacheResponse = readCache<T>()
                    if (cacheResponse.data != null) {
                        // 抛到主线程里面去
                        MainHandler.sendAtFrontOfQueue(runnable = {
                            callback.onSuccess(cacheResponse)
                        })

                        AuiuLog.d("enqueue, cache: " + request.getCacheKey())
                    }
                })
            }

            delegate.enqueue(object : AuiuCallback<T> {
                override fun onSuccess(response: AuiuResponse<T>) {
                    dispatchInterceptor(request, response)
                    saveCacheIfNeed(response)
                    callback.onSuccess(response)
                }

                override fun onFailed(throwable: Throwable) {
                    callback.onFailed(throwable)
                }
            })
        }

        private fun saveCacheIfNeed(response: AuiuResponse<T>) {
            if (request.cacheStrategy == CacheStrategy.CACHE_FIRST || request.cacheStrategy == CacheStrategy.NET_CACHE) {
                if (response.data != null) {
                    AuiuExecutor.executor(runnable = {
                        AuiuStorage.saveCache(request.getCacheKey(), response.data)
                    })
                }
            }
        }

        private fun <T> readCache(): AuiuResponse<T> {
            // AuiuStorage 查询缓存 需要提供一个cacheKey
            val cacheKey = request.getCacheKey()
            val cache = AuiuStorage.getCache<T>(cacheKey)
            val cacheResponse = AuiuResponse<T>()
            cacheResponse.data = cache
            cacheResponse.code = AuiuResponse.CACHE_SUCCESS
            cacheResponse.message = "缓存获取成功"
            return cacheResponse
        }


        private fun dispatchInterceptor(request: AuiuRequest, response: AuiuResponse<T>?) {
            if (interceptors.size <= 0) return
            InterceptorChain(request, response).dispatch()
        }

        internal inner class InterceptorChain(
            private val request: AuiuRequest,
            private val response: AuiuResponse<T>?
        ) : AuiuInterceptor.Chain {
            // 分发的第几个拦截器
            var callIndex: Int = 0
            override val isRequestPeriod: Boolean
                get() = response == null

            override fun request(): AuiuRequest {
                return request
            }

            override fun response(): AuiuResponse<*>? {
                return response
            }

            fun dispatch() {
                val interceptor = interceptors[callIndex]
                val intercept = interceptor.intercept(this)
                callIndex++
                if (!intercept && callIndex < interceptors.size) {
                    dispatch()
                }
            }

        }

    }
}